package current_limiting

import (
	"container/ring"
	"sync"
	"time"
)

// 实现思路
// 1、初始化滑动窗口数量和时间片范围
// 2、创建di

type SlidingWindow struct {
	rate           int // 窗口内最多允许请求数
	begin          time.Time
	windows        int        // 滑动窗口数
	head           *ring.Ring // 环形队列
	cycle          time.Duration
	maxWindowLimit int
	lock           sync.Mutex // 互斥锁，防止并发
}

func (sw *SlidingWindow) New(rate, windows int, cycle time.Duration) {
	sw.rate = rate
	sw.windows = windows
	sw.head = ring.New(windows)
	sw.cycle = cycle
	sw.begin = time.Now()
	sw.maxWindowLimit = rate / windows

	// 初始化队列值
	for i, now := 1, sw.head.Next(); i <= sw.head.Len(); now, i = now.Next(), i+1 {
		now.Value = 0
	}

	// 初始化定时器任务，每秒刷新一次滑动窗口数据
	go func(ss *SlidingWindow) {
		timer := time.NewTicker(time.Millisecond * 200)
		for range timer.C {

			current := time.Now()
			//fmt.Println("比较信息-当前时间窗口请求 ", current.Sub(ss.begin), time.Now().Format("2006-01-02 03:04:05"), time.Now().UnixNano()/1e6)
			if current.Sub(ss.begin) >= ss.cycle {
				//fmt.Println("滑动窗口：")
				//移动当前链表节点到尾部
				ss.head.Value = 0
				for i, now := 1, ss.head.Next(); i <= ss.head.Len(); i, now = i+1, now.Next() {
					ss.head = ss.head.Next()
				}
				for i, now := 1, ss.head.Next(); i <= ss.head.Len(); i, now = i+1, now.Next() {
					//fmt.Println("链表节点：", i, now.Next(), time.Now().Format("2006-01-02 03:04:05"), time.Now().UnixNano()/1e6)
				}
			}
		}
	}(sw)
}

func (sw *SlidingWindow) Allow() bool {
	sw.lock.Lock()
	defer sw.lock.Unlock()
	val := sw.head.Value.(int)
	//fmt.Printf("比较信息-当前时间窗口请求之（%d） - 请求上限（%d）\n", val, sw.maxWindowLimit)
	if sw.Sum() >= sw.rate {
		return false
	} else {
		val++
		sw.head.Value = val
		return true
	}
}

func (sw *SlidingWindow) Sum() int {
	sum := 0
	for i, now := 1, sw.head.Next(); i <= sw.head.Len(); i, now = i+1, now.Next() {
		sum += now.Value.(int)
	}
	return sum
}
